from __future__ import absolute_import, unicode_literals

import logging

import pytest

from psd_tools.api.layers import Group, PixelLayer, ShapeLayer
from psd_tools.api.psd_image import PSDImage
from psd_tools.constants import BlendMode

from ..utils import full_name

logger = logging.getLogger(__name__)


@pytest.fixture
def pixel_layer():
    return PSDImage.open(full_name("layers/pixel-layer.psd"))[0]


@pytest.fixture
def adjustment_layer():
    return PSDImage.open(full_name("layers/brightness-contrast.psd"))[0]


@pytest.fixture
def fill_layer():
    return PSDImage.open(full_name("layers/solid-color-fill.psd"))[0]


@pytest.fixture
def shape_layer():
    return PSDImage.open(full_name("layers/shape-layer.psd"))[0]


@pytest.fixture
def smartobject_layer():
    return PSDImage.open(full_name("layers/smartobject-layer.psd"))[0]


@pytest.fixture
def type_layer():
    return PSDImage.open(full_name("layers/type-layer.psd"))[0]


@pytest.fixture
def group():
    return PSDImage.open(full_name("layers/group.psd"))[0]


ALL_FIXTURES = [
    "pixel_layer",
    "shape_layer",
    "smartobject_layer",
    "type_layer",
    "group",
    "adjustment_layer",
    "fill_layer",
]


def test_pixel_layer_properties(pixel_layer):
    layer = pixel_layer
    assert layer.name == "Pixel", "layer.name = %s" % type(layer.name)
    assert layer.kind == "pixel"
    assert layer.visible is True
    assert layer.opacity == 255
    assert isinstance(layer.parent, PSDImage)
    assert isinstance(layer.blend_mode, BlendMode)
    assert layer.left == 1
    assert layer.top == 1
    assert layer.right == 30
    assert layer.bottom == 30
    assert layer.width == 29
    assert layer.height == 29
    assert layer.size == (29, 29)
    assert layer.bbox == (1, 1, 30, 30)
    assert layer.clip_layers == []
    assert layer.tagged_blocks is not None
    assert layer.layer_id == 3


def test_pixel_layer_writable_properties(pixel_layer):
    layer = pixel_layer
    layer.name = "foo"
    assert layer.name == "foo"
    layer._record.tobytes()
    layer.name = "\ud83d\udc7d"
    assert layer.name == "\ud83d\udc7d"
    layer._record.tobytes()

    layer.visible = False
    assert layer.visible is False

    layer.opacity = 128
    assert layer.opacity == 128

    layer.blend_mode = BlendMode.LINEAR_DODGE
    assert layer.blend_mode == BlendMode.LINEAR_DODGE

    layer.left = 2
    assert layer.left == 2
    layer.top = 2
    assert layer.top == 2
    assert layer.size == (29, 29)

    layer.offset = (1, 1)
    assert layer.offset == (1, 1)
    assert layer.size == (29, 29)


def test_layer_is_visible(pixel_layer):
    assert pixel_layer.is_visible()


@pytest.fixture(params=["pixel_layer", "group"])
def is_group_args(request):
    return (
        request.getfixturevalue(request.param),
        {"pixel_layer": False, "group": True}.get(request.param),
    )


def test_layer_is_group(is_group_args):
    layer, expected = is_group_args
    assert layer.is_group() == expected


def test_layer_has_mask(pixel_layer):
    assert pixel_layer.has_mask() is False


@pytest.fixture(params=ALL_FIXTURES)
def kind_args(request):
    expected = request.param.replace("_layer", "")
    expected = expected.replace("fill", "solidcolorfill")
    expected = expected.replace("adjustment", "brightnesscontrast")
    return (request.getfixturevalue(request.param), expected)


def test_layer_kind(kind_args):
    layer, expected = kind_args
    assert layer.kind == expected


def test_curves_with_vectormask():
    layer = PSDImage.open(full_name("layers/curves-with-vectormask.psd"))[0]
    assert layer.kind == "curves"


@pytest.fixture(params=ALL_FIXTURES)
def topil_args(request):
    is_image = request.param in {
        "pixel_layer",
        "smartobject_layer",
        "type_layer",
        "fill_layer",
        "shape_layer",
    }
    return (request.getfixturevalue(request.param), is_image)


def test_topil(topil_args):
    from PIL.Image import Image

    fixture, is_image = topil_args
    image = fixture.topil()

    channel_ids = [c.id for c in fixture._record.channel_info if c.id >= -1]
    for channel in channel_ids:
        fixture.topil(channel)

    assert isinstance(image, Image) if is_image else image is None


def test_clip_adjustment():
    psd = PSDImage.open(full_name("clip-adjustment.psd"))
    assert len(psd) == 2
    layer = psd[0]
    assert layer.kind == "type"
    assert len(layer.clip_layers) == 1


def test_nested_clipping_layers():
    psd = PSDImage.open(full_name("clipping-mask.psd"))
    psd[1].blend_mode = BlendMode.NORMAL
    psd[1].clipping_layer = True
    assert psd[1].clipping_layer is True
    assert psd[1][1].has_clip_layers()
    assert psd[1][2].clipping_layer
    assert psd[0].has_clip_layers()


def test_type_layer(type_layer):
    assert type_layer.text == "A"
    assert type_layer.transform == (
        1.0000000000000002,
        0.0,
        0.0,
        1.0,
        0.0,
        4.978787878787878,
    )
    assert type_layer.engine_dict
    assert type_layer.resource_dict
    assert type_layer.document_resources
    assert type_layer.warp


def test_group_writable_properties(group):
    assert group.blend_mode == BlendMode.PASS_THROUGH
    group.blend_mode = BlendMode.SCREEN
    assert group.blend_mode == BlendMode.SCREEN


def test_group_extract_bbox():
    psd = PSDImage.open(full_name("hidden-groups.psd"))
    assert Group.extract_bbox(psd[1:], False) == (40, 72, 83, 134)
    assert Group.extract_bbox(psd[1:], True) == (25, 34, 83, 134)


def test_shape_and_fill_layer():
    psd = PSDImage.open(full_name("vector-mask2.psd"))
    for i in range(8):
        assert isinstance(psd[i], ShapeLayer)
    for i in range(8, 10):
        assert isinstance(psd[i], PixelLayer)


def test_has_effects():
    psd = PSDImage.open(full_name("effects/effects-enabled.psd"))
    assert not psd[0].has_effects()
    assert psd[1].has_effects()
    assert not psd[2].has_effects()
    assert not psd[3].has_effects()


def test_bbox_updates():
    psd = PSDImage.open(full_name("hidden-groups.psd"))
    group1 = psd[1]
    group1.visible = False
    assert group1.bbox == (0, 0, 0, 0)
    group1.visible = True
    assert group1.bbox == (25, 34, 80, 88)
